package com.atguigu.gmall.gateway.fillter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.AuthContextHolder;
import com.atguigu.gmall.common.util.IpUtil;
import jdk.nashorn.internal.ir.IfNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @program: gmall-parent
 * @author: Mr.Zhuo
 * @create: 2022-04-13 15:03
 **/
@Component
public class AuthGlobalFilter implements GlobalFilter {

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${authUrls.url}")
    private String authUrls;

    // 匹配路径的工具类
    private AntPathMatcher antPathMatcher = new AntPathMatcher();


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 首先分析 我们访问哪几种路径的时候需要进行拦截
        // 1 /api/list/inner/createIndex
        ServerHttpRequest request = exchange.getRequest();
        // 获取请求路径
        String path = request.getPath().toString();

        // 然后判断当前路径是否包含
        if (antPathMatcher.match("/**/inner/**", path)) {

            ServerHttpResponse response = exchange.getResponse();

            // 然后进行给客户端返回错误信息
            return out(response, ResultCodeEnum.PERMISSION);
        }

        // 进行获取用户的id值
        String userId = this.getUserId(exchange);
        if ("-1".equals(userId)) {
            ServerHttpResponse response = exchange.getResponse();

            // 然后进行给客户端返回错误信息
            return out(response, ResultCodeEnum.ILLEGAL_REQUEST);
        }


        // 2 /api/**/auth/**
        // 当前必须登录之后才可以进行访问
        if (antPathMatcher.match("/**/auth/**", path)) {

            if (StringUtils.isEmpty(userId)) {
                ServerHttpResponse response = exchange.getResponse();

                // 然后进行给客户端返回错误信息
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }

        }

        // 3 就是 trade.html,myOrder.html,list.html,addCart.html
        String[] split = authUrls.split(",");
        if (split != null && split.length > 0) {
            // 然后进行循环遍历
            for (String pathStr : split) {
                if (path.indexOf(pathStr) != -1) {
                    if (StringUtils.isEmpty(userId)) {
                        // 进行页面跳转
                        ServerHttpResponse response = exchange.getResponse();
                        // 1. 设置状态码为302
                        response.setStatusCode(HttpStatus.SEE_OTHER);
                        // 2.设置响应头location
                        response.getHeaders().set(HttpHeaders.LOCATION, "http://www.gmall.com/login.html?originUrl=" + request.getURI());
                        // 3.进行转发
                        return response.setComplete();
                    }
                }
            }
        }

        // 进行获取用户的临时id值
        String userTempId = this.getUserTempId(exchange);

        // 4 如果用户登录之后我们需要把userInfo的id值进行传递给后端
        if (!StringUtils.isEmpty(userId)) {
            // request对象
            request.mutate().header("userId", userId).build();

            // 然后执行带有 请求头的request对象
            return chain.filter(exchange.mutate().request(request).build());
        }
        // 4 如果用户没有登录我们需要把userTempId值进行传递给后端
        if (!StringUtils.isEmpty(userTempId)) {
            // request对象
            request.mutate().header("userTempId", userTempId).build();

            // 然后执行带有 请求头的request对象
            return chain.filter(exchange.mutate().request(request).build());
        }
        // 默认执行哦
        return chain.filter(exchange);
    }

    /**
     * 进行获取用户的临时id值
     *
     * @param exchange
     * @return
     */
    private String getUserTempId(ServerWebExchange exchange) {
        String userTempId = "";
        List<String> strings = exchange.getResponse().getHeaders().get("userTempId");
        if (strings != null && strings.size() > 0) {
            userTempId = strings.get(0);
        }
        if (StringUtils.isEmpty(userTempId)) {
            // 我们从cookie中获取token值
            HttpCookie cookie = exchange.getRequest().getCookies().getFirst("userTempId");
            if (cookie != null) {
                userTempId = cookie.getValue();
                return userTempId;
            }
        }
        return "";
    }

    /**
     * 进行获取用户的id值
     * 我们获取toke值的位置 1响应头 2cookie
     * 从请求头中获取toke 【且我们只放了一个token值，索引0就是我们需要的值】
     *
     * @param exchange
     * @return
     */
    private String getUserId(ServerWebExchange exchange) {
        String token = "";
        List<String> strings = exchange.getRequest().getHeaders().get("token");
        if (strings != null && strings.size() > 0) {
            token = strings.get(0);
        }
        if (StringUtils.isEmpty(token)) {
            // 我们从cookie中获取token值
            HttpCookie cookie = exchange.getRequest().getCookies().getFirst("token");
            if (cookie != null) {
                token = cookie.getValue();
            }
        }
        if (!StringUtils.isEmpty(token)) {
            // 此时我们已经获取到了token
            // 调用redis进行获取id值
            String tokenRedis = (String) redisTemplate.opsForValue().get("user:login:" + token);
            JSONObject object = JSONObject.parseObject(tokenRedis);
            if (!StringUtils.isEmpty(object)) {
                // 获取ip地址
                String infoId = (String) object.get("userId");
                String ipAddress = (String) object.get("ip");
                // 获取用户id  防止盗用token
                if (ipAddress.equals(IpUtil.getGatwayIpAddress(exchange.getRequest()))) {
                    return infoId;
                } else {
                    return "-1";
                }
            }
        }
        return "";
    }

    /**
     * 客户端返回错误信息
     *
     * @param response
     * @param permission
     * @return
     */
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum permission) {
        // 首先将格式转换成我们统一返回数据格式
        Result<Object> result = Result.build(null, permission);

        // 转换成字节数组
        byte[] bytes = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);

        // 流里面需要一个字节数组
        DataBuffer wrap = response.bufferFactory().wrap(bytes);
        // 请求头格式
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        // 我们需要一个流
        return response.writeWith(Mono.just(wrap));
    }
}
